#!/usr/bin/env python
# -*- coding: utf-8 -*-

#     Copyright 2008 OtÃ¡vio Pontes <otaviobp@gmail.com>
#
#     This file is part of amarokfs-fuse.
#
#     amarokfs-fuse is free software: you can redistribute it and/or 
# 	modify it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 3 of the License, or
#     (at your option) any later version.
#
#     amarokfs-fuse is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with amarokfs-fuse.  If not, see 
# 	<http://www.gnu.org/licenses/>.

#TODO:
# - Suportar iso8859-1
# - deixar o usuario selecionar o path do bd do amarok com -o
# - Alguns artistas ficam zuados
# - suportar files e naum somente symlink
# - realizar filtros mais complexos
# - exibir informacoes adicionais dos arquivos

#FUSE STUFF
# pull in some spaghetti to make this stuff work without fuse-py being installed
try:
    import _find_fuse_parts
except ImportError:
    pass
import fuse
from fuse import Fuse
if not hasattr(fuse, '__version__'):
    raise RuntimeError, \
        "your fuse-py doesn't know of fuse.__version__, probably it's too old."
fuse.fuse_python_api = (0, 2)

#begginig of the code
import logging
from logging import warning as log
import os, stat, errno, time
from amarokdb import AmarokDB

class FileStat(fuse.Stat):
    def __init__(self):
        self.st_mode = 0
        self.st_ino = 0
        self.st_dev = 0
        self.st_nlink = 0
        self.st_uid = 0
        self.st_gid = 0
        self.st_size = 0
        self.st_atime = 0
        self.st_mtime = 0
        self.st_ctime = 0

class MusicDictionary (dict):
    """
    Class created to simulate a dict. It represents an sub-tree with the music names.
    """
    def __init__(self, amarokdb, field):
        self.__amarokdb = amarokdb
        self.__field = field
    
    def __getitem__(self, item):
        return ((self.__amarokdb.getMusicTitlesMp3, self.__field, item), {})
        
class GenericDictionary(dict):
    """
    Class used to represent a dictonary, but the keys and values are returned by a function.
    """

    def __init__ (self, keyFunction, keyParam, valueFunction, valueParam):
        self.__keyFunction = keyFunction
        self.__keyParam = keyParam
        self.__valueFunction = valueFunction
        self.__valueParam = valueParam

    def keys(self):
        if self.__keyFunction == None:
            dict.keys(self)
        else:
            if self.__keyParam == None: 
                return self.__keyFunction();
            else:
                return self.__keyFunction(keyParam);

    def __getitem__(self, item):
        if self.__valueFunction == None:
            return dict.__getitem__(self, item)
        else:
            if self.__valueParam == None:
                return self.__valueFunction(item)
            else:
                return self.__valueFunction(self.__valueParam, item)

class AmarokFS(Fuse):
    def __init__(self, usage, version, dash_s_do):
        Fuse.__init__(self, usage=usage, version=version, dash_s_do=dash_s_do)
        self.__root="/"
        self.__ARTIST = "Artists"
        self.__ALBUM = "Albums"
        self.__GENRE = "Genres"
        self.__amarokdb = AmarokDB("collection.db")

        #dictionary of Dictionarys
        #The keys of the dictionarys are the files in current folder. If the file is a folder, then its value is a dict with
        # the contents of the sub-folder. If its a regular file, the content is None

        #obs. para funcionar em multiplos niveis tem que pensar em como fazer o filtro. Isto é, guardar todos os dados que jah foram filtrados
        self.__pathTree ={"": 
                          {"All": GenericDictionary(self.__amarokdb.getMusicTitlesMp3,None, None, None),
                           self.__ARTIST: GenericDictionary(self.__amarokdb.getArtistNames,None, self.__amarokdb.getMusicTitlesAndUrl,"Artist"),
                           self.__ALBUM: GenericDictionary(self.__amarokdb.getAlbumNames,None,self.__amarokdb.getMusicTitlesAndUrl,"Album"),
                           self.__GENRE: GenericDictionary(self.__amarokdb.getGenreNames,None,self.__amarokdb.getMusicTitlesAndUrl,"Genre"),
                           }}

    #Fuse Functions

    def getattr(self, path):
        try:
            file = self.__findPath(path)
        except:
            return -errno.ENOENT

        st = FileStat()
        
        try:
            filetype=file["__filetype"]
        except:
            filetype='directory'

        if filetype == 'symlink':
             st.st_mode = stat.S_IFLNK | 0444
             st.st_nlink = 1
             st.st_size = 0
        else:
            st.st_mode = stat.S_IFDIR | 0444
            st.st_nlink = 2
        return st

    def __findPath(self, path):
        #split the path
        path_splited = path.split(os.sep)
        #fix a bug. When path='/', '' its duplicated
        if path_splited == ['','']:
            path_splited = ['']

        tree = self.__pathTree
        #find in the tree the sub-tree with the path
        for i in path_splited:
            tree = tree[i]
        return tree

    def readdir(self, path, offset):
        #the . and .. dirs
        yield fuse.Direntry(".")
        yield fuse.Direntry("..")

        tree = self.__findPath(path)
        for i in tree.keys():
            if i != "": 
                yield fuse.Direntry(i)

    def readlink(self, path):
        log('read')
        try:
            file = self.__findPath(path)
            log (file['__url'])
            return str(file['__url'])
        except:
            return -errno.ENOENT
                
#     def open(self, path, flags):
#         if path != hello_path:
#             return -errno.ENOENT
#         accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
#         if (flags & accmode) != os.O_RDONLY:
#             return -errno.EACCES

#     def read(self, path, size, offset):
#         if path != hello_path:
#             return -errno.ENOENT
#         slen = len(hello_str)
#         if offset < slen:
#             if offset + size > slen:
#                 size = slen - offset
#             buf = hello_str[offset:offset+size]
#         else:
#             buf = ''
#         return buf

def main():
    logging.getLogger().addHandler(logging.FileHandler('arq'))
    log('Inicio amarokfs')
    
    usage="""
Userspace hello example
""" + Fuse.fusage
    server = AmarokFS(version="%prog " + fuse.__version__,
                          usage=usage,
                          dash_s_do='setsingle')
        
    server.parse(errex=1)
    server.flags = 0
    server.multithreaded = False;
    server.main()
    log('fim amarokfs\n\n')

if __name__ == '__main__':
    main()
    
